//
//  TCPClient.m
//  LazyHome
//
//  Created by yh on 2018/3/30.
//  Copyright © 2018年 yh. All rights reserved.
//

#import "TCPClient.h"
#import "LHManager.h"

@interface TCPClient(){
    NSInteger tagIndex;
}

@property (nonatomic,strong) NSTimer *sendTimeTriger;

@property (nonatomic,strong) NSTimer *readTimeTriger;


@end
@implementation TCPClient

+ (instancetype)clientWithHost:(NSString *)host
                          port:(NSString *)port
                     localport:(NSString *)localport
                         error:(NSError *__autoreleasing *)errPtr
{
    TCPClient *client = [[TCPClient alloc] initWithHost:host port:port localport:localport error:errPtr];
    return client;
}

- (instancetype)initWithHost:(NSString *)host
                        port:(NSString *)port
                   localport:(NSString *)localport
                       error:(NSError *__autoreleasing *)errPtr{
    self = [super init];
    if (self) {
        self.host = host;
        self.port = port;
        self.localport = localport;
        self.sendBuffer = [[NSMutableData alloc] init];
        self.reciveBuffer = [[NSMutableData alloc] init];
        self.sendtagData = [[NSMutableDictionary alloc]init];
        [[AFNetworkReachabilityManager sharedManager] startMonitoring];
        [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(netChangehandle:) name:AFNetworkingReachabilityDidChangeNotification object:nil];
        self.tcpSocket = [[GCDAsyncSocket alloc] initWithDelegate:self delegateQueue:dispatch_get_main_queue()];
        self.tcpSocket.delegate = self;
        [self.tcpSocket acceptOnPort:[self.localport integerValue] error:errPtr];
        [self addObserver:self forKeyPath:@"tcpClientStatus" options:NSKeyValueObservingOptionOld | NSKeyValueObservingOptionNew context:nil];
        self.tcpClientStatus = TCPClientStatusInit;
        
 
    }
    return self;
}

- (void)connectWithError:(NSError *__autoreleasing *)errPtr{
    if (self.tcpClientStatus == TCPClientStatusInit) {
        self.tcpClientStatus = TCPClientStatusOnConnecting;
        NSLog(@"连接服务器中");
    }else{
        self.tcpClientStatus = TCPClientStatusOnReconnecting;
        NSLog(@"重新连接服务器");
    }
    [self.tcpSocket disconnect];
    [self.tcpSocket connectToHost:self.host onPort:[self.port integerValue] error:errPtr];
    if (*errPtr) {
        NSLog(@"连接 host失败 ：%@",*errPtr);
    }
}

- (void)normal_disConnectByUser{
    [self.tcpSocket disconnect];
}


#pragma mark - 控制
- (void)sendData:(NSData *)data withTimeout:(NSTimeInterval)timeout tag:(long)tag{
    [self.sendtagData setObject:data forKey:@(tagIndex)];
    [self.tcpSocket writeData:data withTimeout:-1 tag:tagIndex];
    tagIndex ++;
    tagIndex = tagIndex == sizeof(long) - 1 ? 0 : tagIndex;
    if (self.sendTimeTriger) {
        [self.sendTimeTriger setFireDate:[NSDate distantFuture]];
        [self.sendTimeTriger invalidate];
        self.sendTimeTriger = nil;
    }
    self.sendTimeTriger = [NSTimer scheduledTimerWithTimeInterval:10 repeats:YES block:^(NSTimer * _Nonnull timer) {
        if (self.delegate && [self.delegate respondsToSelector:@selector(tcpClient:writeChannelFreeInTimer:)]) {
            [self.delegate tcpClient:self writeChannelFreeInTimer:timer];
        }
    }];
    
}



#pragma mark - 网络状态监听

- (void)netChangehandle:(NSNotification *)noti{
    NSDictionary *dic = noti.userInfo;
    //获取网络状态
    NSInteger status = [[dic objectForKey:@"AFNetworkingReachabilityNotificationStatusItem"] integerValue];
    if (self.delegate && [self.delegate respondsToSelector:@selector(tcpClient:shouldReconnectForNetChange:)]) {
        if ([self.delegate tcpClient:self shouldReconnectForNetChange:status]) {
//            [self connectWithError:nil]; //网络切换后重新连接
        }
    }
    switch (status) {
        case AFNetworkReachabilityStatusNotReachable:{
            NSLog(@"无网络连接");
        }
            break;
        case AFNetworkReachabilityStatusReachableViaWWAN:{
            NSLog(@"移动蜂窝网络");
        }
            break;
        case AFNetworkReachabilityStatusReachableViaWiFi:{
            
            NSLog(@"wifi网络");
        }
            break;
        case AFNetworkReachabilityStatusUnknown:{
            
            NSLog(@"无法获取网络状态");
        }
            break;
        default:
            break;
    }
}

#pragma mark - GCDAsyncSocketDelegate

- (void)socket:(GCDAsyncSocket *)sock didConnectToHost:(NSString *)host port:(uint16_t)port{
    self.tcpClientStatus = TCPClientStatusConnected;

    if (self.tcpClientStatus == TCPClientStatusOnConnecting) {
        NSLog(@"连接服务器成功");
    }else if (self.tcpClientStatus == TCPClientStatusOnReconnecting) {
        NSLog(@"重新连接服务器成功");
    }
    [sock readDataWithTimeout:-1 tag:1001]; //连上后不断循环读取数据
}


- (void)socketDidDisconnect:(GCDAsyncSocket *)sock withError:(nullable NSError *)err{
    [self clearTriger];
    if (err) {
        self.tcpClientStatus = TCPClientStatusConnectException;
        if (err.code == 61 && err.domain ==  NSPOSIXErrorDomain) {
            NSLog(@"服务器拒绝连接");
        }else if (err.code == 0 && err.domain ==  NSPOSIXErrorDomain) {
            NSLog(@"服务器器异常连接不上");
        }else if (err.code == 53 && err.domain ==  NSPOSIXErrorDomain) {
            NSLog(@"Software caused connection abort");
        }else if (err.code == 2 && err.domain ==  NSPOSIXErrorDomain) {
            NSLog(@"No such file or directory");
        }
     
    }else{
        self.tcpClientStatus = TCPClientStatusDisConnect;
    }
}


- (void)socket:(GCDAsyncSocket *)sock didReadData:(NSData *)data withTag:(long)tag{
    [self.reciveBuffer appendData:data];
    if (self.delegate && [self.delegate respondsToSelector:@selector(tcpClient:socket:didReadData:withTag:)]) {
        [self.delegate tcpClient:self socket:sock didReadData:data withTag:tag];
    }
    [sock readDataWithTimeout:-1 tag:tag];//不断循环读取数据
    
    
    if (self.readTimeTriger) {
        [self.readTimeTriger setFireDate:[NSDate distantFuture]];
        [self.readTimeTriger invalidate];
        self.readTimeTriger = nil;
    }
    self.readTimeTriger = [NSTimer scheduledTimerWithTimeInterval:30 repeats:YES block:^(NSTimer * _Nonnull timer) {
        if (self.delegate && [self.delegate respondsToSelector:@selector(tcpClient:writeChannelFreeInTimer:)]) {
            [self.delegate tcpClient:self readChannelFreeInTimer:timer];
        }
    }];
    
}

- (void)socket:(GCDAsyncSocket *)sock didWriteDataWithTag:(long)tag{
    NSData *data = self.sendtagData[@(tag)];
    if (self.delegate && [self.delegate respondsToSelector:@selector(tcpClient:socket:didWriteData:withTag:)]) {
        [self.delegate tcpClient:self socket:sock didWriteData:data withTag:tag];
    }
    [self.sendtagData removeObjectForKey:@(tag)];
}
- (void)dealloc{
    NSLog(@"tcp client with server : %@:%@ is destory",self.host,self.port);
}


- (void)clearTriger{
    
    if (self.sendTimeTriger) {
        [self.sendTimeTriger setFireDate:[NSDate distantFuture]];
        [self.sendTimeTriger invalidate];
        self.sendTimeTriger = nil;
    }
    if (self.readTimeTriger) {
        [self.readTimeTriger setFireDate:[NSDate distantFuture]];
        [self.readTimeTriger invalidate];
        self.readTimeTriger = nil;
    }
};


- (void)resetConnect{
    [self.tcpSocket disconnect];
    [self clearTriger];
}

- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSKeyValueChangeKey,id> *)change context:(void *)context{
    if ([keyPath isEqualToString:@"tcpClientStatus"]) {
        TCPClientStatus oldStatus = [change[@"old"] integerValue];
        TCPClientStatus latStatus = [change[@"new"] integerValue];
        if (self.delegate && [self.delegate respondsToSelector:@selector(tcpClient:connectStatusChange:)]) {
            [self.delegate tcpClient:self connectStatusChange:latStatus];
        }
    }
}

@end
